﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace MonoPhysics
{
    public delegate bool LinkedListCallback<T, K>(T element, K innerParent, short willChangeColor, T nextElement, K nextInnerParent);  // -1 = autre couleur => couleur courant / 0 = rien / +1 = couleur courante => autre couleur
    public delegate bool LinkedListBlackCallback<T, K>(T element, T nextElement); // callback pour le parcours noir
    public class CircularLinkedListNode<T, K>
    {
        public enum NodeType { PointSortant, PointEntrant, pointNeutre };
        public NodeType pointType = NodeType.pointNeutre;
        public T value;
        public K innerParent;
        public List<CircularLinkedListColoredNode<T, K>> nodes = new List<CircularLinkedListColoredNode<T, K>>();
        public int FondamentalColor
        { get { return this.nodes[0].couleur; } }

        public CircularLinkedListNode()
        { }

        public CircularLinkedListNode(T value, K InnerParent)
        {
            this.nodes = new List<CircularLinkedListColoredNode<T, K>>(2);
            this.value = value;
            this.innerParent = InnerParent;
        }

        public static CircularLinkedListNode<T, K> BuildColoredCircularLinkedListNode(List<T> values, K InnerParent, int FondamentalColor)
        {
            // comme on travaille avec des polygones on ne devrait pas avoir des tableau vides ou avec moins de 3 points!
            // On construit une liste a son etat initial: elle possedes des liens noirs et de couleur IDENTIQUES!
            // Le lien de couleur represente le lien de couleur fondamentale.
            CircularLinkedListNode<T, K> head = null;
            CircularLinkedListNode<T, K> parent;

            int l = values.Count;
            // Les deux premiers cas sont des cas particuliers!
            if (l >= 1)
            {
                T e = values[0];
                // Noeud couleur
                CircularLinkedListNode<T, K> c = new CircularLinkedListNode<T, K>();
                c.value = e;
                c.innerParent = InnerParent;
                CircularLinkedListColoredNode<T, K> cn = new CircularLinkedListColoredNode<T, K>();
                cn.couleur = FondamentalColor;
                cn.parent = c;
                cn.fils = c;
                cn.innerPosition = float.MinValue / 3;
                // fin Noeud couleur
                // Noeud noir
                CircularLinkedListColoredNode<T, K> cn2 = new CircularLinkedListColoredNode<T, K>();
                cn2.couleur = 0;
                cn2.parent = cn.parent;
                cn2.fils = cn.fils;
                cn2.innerPosition = cn.innerPosition;
                // fin Noeud noir
                c.nodes.Add(cn); // on ajoute le lien colore avant le lien noir (car il faut la couleur fondamentale dans la case [0]
                c.nodes.Add(cn2);
                head = c;
                if (l >= 2)
                {
                    e = values[1];
                    // Noeud couleur
                    c = new CircularLinkedListNode<T, K>();
                    c.value = e;
                    c.innerParent = InnerParent;
                    cn = new CircularLinkedListColoredNode<T, K>();
                    cn.couleur = FondamentalColor;
                    cn.parent = head;
                    cn.fils = head;
                    cn.parent.nodes[0].fils = c;
                    cn.parent.nodes[0].parent = c;
                    cn.parent.nodes[1].fils = c;
                    cn.parent.nodes[1].parent = c;
                    cn.innerPosition = float.MaxValue / 2;
                    // fin Noeud couleur
                    // Noeud noir
                    cn2 = new CircularLinkedListColoredNode<T, K>();
                    cn2.couleur = 0;
                    cn2.parent = cn.parent;
                    cn2.fils = cn.fils;
                    cn2.innerPosition = cn.innerPosition;
                    // fin Noeud noir
                    c.nodes.Add(cn); // on ajoute le lien colore avant le lien noir (car il faut la couleur fondamentale dans la case [0]
                    c.nodes.Add(cn2);
                    parent = c;
                    for (int i = 2; i < l; i++)
                    {
                        e = values[i];
                        // Noeud couleur
                        c = new CircularLinkedListNode<T, K>();
                        c.value = e;
                        c.innerParent = InnerParent;
                        cn = new CircularLinkedListColoredNode<T, K>();
                        cn.couleur = FondamentalColor;
                        cn.parent = parent;
                        cn.fils = parent.nodes[0].fils;
                        cn.fils.nodes[0].parent = c;
                        cn.parent.nodes[0].fils = c;
                        cn.fils.nodes[1].parent = c;
                        cn.parent.nodes[1].fils = c;
                        cn.innerPosition = (parent.nodes[0].innerPosition + cn.fils.nodes[0].innerPosition) / 2;
                        // fin Noeud couleur
                        // Noeud noir
                        cn2 = new CircularLinkedListColoredNode<T, K>();
                        cn2.couleur = 0;
                        cn2.parent = cn.parent;
                        cn2.fils = cn.fils;
                        cn2.innerPosition = cn.innerPosition;
                        // fin Noeud noir
                        c.nodes.Add(cn); // on ajoute le lien colore avant le lien noir (car il faut la couleur fondamentale dans la case [0]
                        c.nodes.Add(cn2);
                        parent = c;
                    }
                }
            }
            return head; // Faut pas perdre la tete :p
        }
        #region Parcours
        public void Parcours_FilsPere(int parcColor, LinkedListCallback<T, K> callback)
        {
            CircularLinkedListNode<T, K> parc;
            parc = this;
            do
            {
                bool foundcolor = false;
                int l = parc.nodes.Count;
                CircularLinkedListColoredNode<T, K> ls = null; // de toutes maniere ca ne sera pas nul!
                for (int i = 0; i < l; i++)
                {
                    ls = parc.nodes[i];
                    if (ls.couleur == parcColor)
                    {
                        foundcolor = true;
                        break;
                    }
                }
                short id = 0;
                if (ls.fils.pointType == CircularLinkedListNode<T, K>.NodeType.PointSortant)
                    id = 1;
                else if (parc.pointType == CircularLinkedListNode<T, K>.NodeType.PointEntrant)
                    id = -1;
                if (foundcolor)
                {

                    if (parc.pointType == CircularLinkedListNode<T, K>.NodeType.PointSortant)
                    {
                        if (!callback(parc.value, parc.innerParent, id, parc.nodes[0].parent.value, parc.nodes[0].parent.innerParent))
                            return;
                        parc = parc.nodes[0].parent;
                    }
                    else //(parc.pointType == CircularLinkedListNode<T, K>.NodeType.PointEntrant)
                    {
                        if (!callback(parc.value, parc.innerParent, id, ls.fils.value, ls.fils.innerParent))
                            return;
                        parc = ls.fils;
                    }
                }
                else
                {
                    if (!callback(parc.value, parc.innerParent, id, parc.nodes[0].parent.value, parc.nodes[0].parent.innerParent))
                        return;
                    parc = parc.nodes[0].parent;
                }
            } while (parc != this);
        }
        public void Parcours_FilsFils(int parcColor, LinkedListCallback<T, K> callback)
        {
            CircularLinkedListNode<T, K> parc;
            parc = this;
            do
            {
                short id = 0;
                if (parc.nodes[0].fils.FondamentalColor != parc.FondamentalColor)
                {
                    if (parc.FondamentalColor == parcColor)
                        id = 1;
                    else
                        id = -1;
                }
                bool foundcolor = false;
                int l = parc.nodes.Count;
                CircularLinkedListColoredNode<T, K> ls = null;
                for (int i = 0; i < l; i++)
                {
                    ls = parc.nodes[i];
                    if (ls.couleur == parcColor)
                    {
                        foundcolor = true;
                        break;
                    }
                }
                if (foundcolor)
                {
                    if (!callback(parc.value, parc.innerParent, id, ls.fils.value, ls.fils.innerParent))
                        return;
                    parc = ls.fils;
                }
                else
                {
                    if (!callback(parc.value, parc.innerParent, id, parc.nodes[0].parent.value, parc.nodes[0].parent.innerParent))
                        return;
                    parc = parc.nodes[0].fils;
                }
            } while (parc != this);
        }
        public void Parcours_Noir(LinkedListBlackCallback<T, K> callback)
        {
            CircularLinkedListNode<T, K> parc;
            parc = this;
            do
            {
                if (!callback(parc.value, parc.nodes[1].fils.value))
                    return;
                parc = parc.nodes[1].fils;  // le lien 1 represente la lien noir.
            } while (parc != this);
        }
        #endregion
    }

    public class CircularLinkedListColoredNode<T, K>
    {
        public CircularLinkedListNode<T, K> parent;
        public CircularLinkedListNode<T, K> fils;
        public int couleur;
        public float innerPosition;
    }
}